#include "ffmpeg_service.h"
#include "av_demuxer.h"


#ifdef  _MSC_VER

#ifdef _DEBUG
#pragma comment(lib, "lib/protobuf/debug/lib/libprotobufd.lib")
#else
#pragma comment(lib, "lib/protobuf/lib/libprotobuf.lib")
#endif // _DEBUG

#pragma comment(lib, "lib/ffmpeg/lib/avformat.lib")
#pragma comment(lib, "lib/ffmpeg/lib/avcodec.lib")
#pragma comment(lib, "lib/ffmpeg/lib/avutil.lib")
#pragma comment(lib, "lib/ffmpeg/lib/avdevice.lib")
#pragma comment(lib, "lib/ffmpeg/lib/avfilter.lib")
#pragma comment(lib, "lib/ffmpeg/lib/swresample.lib")
#pragma comment(lib, "lib/ffmpeg/lib/swscale.lib")

#endif



class myFFmpegServiceServiceImpl : public FFmpegServiceServiceImpl
{
public:


    virtual ffmpeg::CRPCProtocol_ErrorCode SayHello(const ffmpeg::HelloRequest& request, ffmpeg::HelloReply& response) {
        response.set_message("hello " + request.name());
        return ffmpeg::CRPCProtocol_ErrorCode_OK;;
    }

    virtual ffmpeg::CRPCProtocol_ErrorCode DemuxerOpen(const ffmpeg::DemuxerOpenRequest& request, ffmpeg::DemuxerOpenReply& response) {
        AVDemuxer* demuxer = new AVDemuxer();
        std::string url = request.url();
        std::string error;
        if (demuxer->Open(url, error)) {
            auto context = response.mutable_context();
            auto format = demuxer->GetFormatContext();

            context->set_duration(format->duration);
            context->set_max_analyze_duration(format->max_analyze_duration);
            context->set_start_time(format->start_time);
            context->set_start_time_realtime(format->start_time_realtime);

            const AVDictionary* m = format->metadata;
            const AVDictionaryEntry* e = NULL;
            while (e = av_dict_iterate(m, e)) {
                auto object = context->add_metadata();
                object->set_key(e->key);
                object->set_value(e->value);
            }

            for (size_t i = 0; i < format->nb_streams; i++)
            {
                auto addStream = context->add_streams();
                auto stream = format->streams[i];

                addStream->set_index(stream->index);
                addStream->set_duration(stream->duration);
                addStream->set_start_time(stream->start_time);
                addStream->set_pts_wrap_bits(stream->pts_wrap_bits);

                if (stream->codecpar != NULL) {
                    auto v = stream->codecpar;
                    auto d = addStream->mutable_codecpar();
                    d->set_codec_type(ffmpeg::AVMediaType(v->codec_type));
                    d->set_codec_id(ffmpeg::AVCodecID(v->codec_id));
                    d->set_codec_tag(v->codec_tag);
                    if (v->extradata != NULL && v->extradata_size > 0) {
                        std::string extradata((char*)v->extradata, v->extradata_size);
                        d->set_extradata(extradata);
                    }
                    if (v->codec_type == AVMediaType::AVMEDIA_TYPE_VIDEO) {
                        d->set_format_video(ffmpeg::AVPixelFormat(v->format));

                        auto dd = d->mutable_sample_aspect_ratio();
                        dd->set_den(v->sample_aspect_ratio.den);
                        dd->set_num(v->sample_aspect_ratio.num);

                        auto ddd = d->mutable_framerate();
                        ddd->set_den(v->framerate.den);
                        ddd->set_num(v->framerate.num);
                    }
                    else if (v->codec_type == AVMediaType::AVMEDIA_TYPE_AUDIO) {
                        d->set_format_audio(ffmpeg::AVSampleFormat(v->format));

                        auto dd = d->mutable_ch_layout();
                        dd->set_nb_channels(v->ch_layout.nb_channels);
                    }
                    d->set_bit_rate(v->bit_rate);
                    d->set_bits_per_coded_sample(v->bits_per_coded_sample);
                    d->set_bits_per_raw_sample(v->bits_per_raw_sample);
                    d->set_profile(v->profile);
                    d->set_level(v->level);
                    d->set_width(v->width);
                    d->set_height(v->height);
                    d->set_video_delay(v->video_delay);
                    d->set_sample_rate(v->sample_rate);
                    d->set_block_align(v->block_align);
                    d->set_frame_size(v->frame_size);
                    d->set_initial_padding(v->initial_padding);
                    d->set_trailing_padding(v->trailing_padding);
                    d->set_seek_preroll(v->seek_preroll);
                }

                auto time_base = addStream->mutable_time_base();
                time_base->set_den(stream->time_base.den);
                time_base->set_num(stream->time_base.num);

                addStream->set_start_time(stream->start_time);
                addStream->set_duration(stream->duration);
                addStream->set_nb_frames(stream->nb_frames);

                auto aspect_ratio = addStream->mutable_sample_aspect_ratio();
                aspect_ratio->set_den(stream->sample_aspect_ratio.den);
                aspect_ratio->set_den(stream->sample_aspect_ratio.num);


                const AVDictionary* m = stream->metadata;
                const AVDictionaryEntry* e = NULL;
                while (e = av_dict_iterate(m, e)) {
                    auto object = addStream->add_metadata();
                    object->set_key(e->key);
                    object->set_value(e->value);
                }

                auto avg_frame_rate = addStream->mutable_avg_frame_rate();
                avg_frame_rate->set_den(stream->avg_frame_rate.den);
                avg_frame_rate->set_num(stream->avg_frame_rate.num);

                auto r_frame_rate = addStream->mutable_r_frame_rate();
                r_frame_rate->set_den(stream->r_frame_rate.den);
                r_frame_rate->set_num(stream->r_frame_rate.num);

                addStream->set_pts_wrap_bits(stream->pts_wrap_bits);
            }


            response.set_code(ffmpeg::CodeOK);
            response.set_handle(int64_t(demuxer));
        }
        else {
            delete demuxer;
            response.set_code(ffmpeg::CodeFail);
            response.set_msg(error);
        }


        return ffmpeg::CRPCProtocol_ErrorCode_OK;
    }

    virtual ffmpeg::CRPCProtocol_ErrorCode DemuxerClose(const ffmpeg::DemuxerCloseRequest& request, ffmpeg::DemuxerCloseReply& response) {
        AVDemuxer* demuxer = static_cast<AVDemuxer*>((void*)request.handle());
        if (demuxer != NULL) {
            demuxer->Close();
            response.set_code(ffmpeg::CodeOK);
        }
        else {
            response.set_code(ffmpeg::CodeFail);
            response.set_msg("null");
        }

        return ffmpeg::CRPCProtocol_ErrorCode_OK;
    }

    virtual ffmpeg::CRPCProtocol_ErrorCode DemuxerReadPacket(const ffmpeg::DemuxerReadPacketRequest& request, ffmpeg::DemuxerReadPacketReply& response) {

        AVDemuxer* demuxer = static_cast<AVDemuxer*>((void*)request.handle());
        if (demuxer == NULL) {
            response.set_code(ffmpeg::CodeFail);
            response.set_msg("null");

            return ffmpeg::CRPCProtocol_ErrorCode_OK;
        }

        AVPacket av_packet1, *av_packet = &av_packet1;

        if (0 == demuxer->Read(av_packet))
        {
            auto pkt = response.mutable_packet();
            pkt->set_pts(av_packet->pts);
            pkt->set_dts(av_packet->dts);
            pkt->set_data(av_packet->data, av_packet->size);
            pkt->set_stream_index(av_packet->stream_index);
            pkt->set_flags(av_packet->flags);
            pkt->set_flagkey(av_packet->flags & AV_PKT_FLAG_KEY);
            pkt->set_flagcorrupt(av_packet->flags & AV_PKT_FLAG_CORRUPT);
            pkt->set_flagdiscard(av_packet->flags & AV_PKT_FLAG_DISCARD);
            pkt->set_flagtrusted(av_packet->flags & AV_PKT_FLAG_TRUSTED);
            pkt->set_flagdisposable(av_packet->flags & AV_PKT_FLAG_DISCARD);
            pkt->set_duration(av_packet->duration);
            pkt->set_pos(av_packet->pos);

            auto time_base = pkt->mutable_time_base();
            time_base->set_den(av_packet->time_base.den);
            time_base->set_num(av_packet->time_base.num);

            response.set_code(ffmpeg::CodeOK);
        }
        else {
            if (demuxer->IsEOF()) {
                response.set_code(ffmpeg::CodeEOF);
            }
            else {
                response.set_code(ffmpeg::CodeFail);
            }
        }

        av_packet_unref(av_packet);


        return ffmpeg::CRPCProtocol_ErrorCode_OK;
    }
};





class InitApp
{
public:
    InitApp() {

        static myFFmpegServiceServiceImpl insFFmpegServiceServiceImpl;
        SetFFmpegServiceServiceImpl(&insFFmpegServiceServiceImpl);

    }
}_init_app;




